package advanced;

import JaCoP.constraints.Cumulative;
import JaCoP.constraints.Max;
import JaCoP.constraints.Sum;
import JaCoP.constraints.XgteqY;
import JaCoP.core.IntVar;
import JaCoP.core.Store;
import JaCoP.search.DepthFirstSearch;
import JaCoP.search.IndomainMin;
import JaCoP.search.Search;
import JaCoP.search.SelectChoicePoint;
import JaCoP.search.SimpleSelect;
import JaCoP.search.SmallestMax;

public class Jobshop2 {
	private static Store store = new Store();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		long T1, T2, T;
		T1 = System.currentTimeMillis();
		scheduleJobs();
		T2 = System.currentTimeMillis();
		T = T2 - T1;
		System.out.println("\n\t*** Execution time = " + T + " ms");
	}

	private static void scheduleJobs() {
		System.out.println("Creating variables and imposing constraints...");
		IntVar[][][] tasks = new IntVar[6][6][2]; // jobs, tasks, [machine,
													// duration]
		int[] durations = { 1, 3, 6, 7, 3, 6, 8, 5, 10, 10, 10, 4, 5, 4, 8, 9,
				1, 7, 5, 5, 5, 3, 8, 9, 9, 3, 5, 4, 3, 1, 3, 3, 9, 10, 4, 1 };
		int sumDurations = 0;
		for (int i : durations) {
			sumDurations += i;
		}
		int[] machineIDs = { 2, 0, 1, 3, 5, 4, 1, 2, 4, 5, 0, 3, 2, 3, 5, 0, 1,
				4, 1, 0, 2, 3, 4, 5, 2, 1, 4, 5, 0, 3, 1, 3, 5, 0, 4, 2 };
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				tasks[job][task][0] = new IntVar(store, "J" + job + "T" + task
						+ "M" + machineIDs[job * 6 + task], machineIDs[job * 6
						+ task], machineIDs[job * 6 + task]);
				tasks[job][task][1] = new IntVar(store, "J" + job + "T" + task
						+ "D" + durations[job * 6 + task], durations[job * 6
						+ task], durations[job * 6 + task]);
			}
		}

		IntVar[] jobTimeOrigins = new IntVar[6 * 6];
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				jobTimeOrigins[job * 6 + task] = new IntVar(store, "OriJ" + job
						+ "T" + task, 0, sumDurations);
			}
		}

		IntVar[] jobTimeEndings = new IntVar[6 * 6];
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				jobTimeEndings[job * 6 + task] = new IntVar(store, "EndJ" + job
						+ "T" + task, 0, sumDurations);
				store.impose(new Sum(new IntVar[] {
						jobTimeOrigins[job * 6 + task], tasks[job][task][1] },
						jobTimeEndings[job * 6 + task]));
			}
		}
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 5; task++) {
				store.impose(new XgteqY(jobTimeOrigins[job * 6 + task + 1],
						jobTimeEndings[job * 6 + task]));
			}
		}

		IntVar resource = new IntVar(store, "", 1, 1);
		IntVar[] resourcesM = { resource, resource, resource, resource,
				resource, resource };
		
		// Map tasks to machines
		int[] oM0Values = { 1, 10, 15, 19, 28, 33 };
		int[] oM1Values = { 2, 6, 16, 18, 25, 30 };
		int[] oM2Values = { 0, 7, 12, 20, 24, 35 };
		int[] oM3Values = { 3, 11, 13, 21, 29, 31 };
		int[] oM4Values = { 5, 8, 17, 22, 26, 34 };
		int[] oM5Values = { 4, 9, 14, 23, 27, 32 };
		
		//Create vectors for cumulative constraints
		IntVar[] originsM0 = new IntVar[6];
		IntVar[] durationsM0 = new IntVar[6];
		IntVar[] originsM1 = new IntVar[6];
		IntVar[] durationsM1 = new IntVar[6];
		IntVar[] originsM2 = new IntVar[6];
		IntVar[] durationsM2 = new IntVar[6];
		IntVar[] originsM3 = new IntVar[6];
		IntVar[] durationsM3 = new IntVar[6];
		IntVar[] originsM4 = new IntVar[6];
		IntVar[] durationsM4 = new IntVar[6];
		IntVar[] originsM5 = new IntVar[6];
		IntVar[] durationsM5 = new IntVar[6];

		//Put values in vectors
		for (int i = 0; i < 6; i++) {
			// For machine 0
			originsM0[i] = jobTimeOrigins[oM0Values[i]];
			durationsM0[i] = tasks[oM0Values[i] / 6][oM0Values[i] % 6][1];
			// For machine 1
			originsM1[i] = jobTimeOrigins[oM1Values[i]];
			durationsM1[i] = tasks[oM0Values[i] / 6][oM1Values[i] % 6][1];
			// For machine 2
			originsM2[i] = jobTimeOrigins[oM2Values[i]];
			durationsM2[i] = tasks[oM0Values[i] / 6][oM2Values[i] % 6][1];
			// For machine 3
			originsM3[i] = jobTimeOrigins[oM3Values[i]];
			durationsM3[i] = tasks[oM0Values[i] / 6][oM3Values[i] % 6][1];
			// For machine 4
			originsM4[i] = jobTimeOrigins[oM4Values[i]];
			durationsM4[i] = tasks[oM0Values[i] / 6][oM4Values[i] % 6][1];
			// For machine 5
			originsM5[i] = jobTimeOrigins[oM5Values[i]];
			durationsM5[i] = tasks[oM0Values[i] / 6][oM5Values[i] % 6][1];
		}
		
		// Impose constraints
		store.impose(new Cumulative(originsM0, durationsM0, resourcesM,
				new IntVar(store, "", 1, 1)));
		store.impose(new Cumulative(originsM1, durationsM1, resourcesM,
				new IntVar(store, "", 1, 1)));
		store.impose(new Cumulative(originsM2, durationsM2, resourcesM,
				new IntVar(store, "", 1, 1)));
		store.impose(new Cumulative(originsM3, durationsM3, resourcesM,
				new IntVar(store, "", 1, 1)));
		store.impose(new Cumulative(originsM4, durationsM4, resourcesM,
				new IntVar(store, "", 1, 1)));
		store.impose(new Cumulative(originsM5, durationsM5, resourcesM,
				new IntVar(store, "", 1, 1)));

		//Impose cost constraint
		IntVar maxEnding = new IntVar(store, "MaxEnd", 0, sumDurations);
		store.impose(new Max(jobTimeEndings, maxEnding));

		Search<IntVar> label = new DepthFirstSearch<IntVar>();

		SelectChoicePoint<IntVar> select = new SimpleSelect<IntVar>(
				jobTimeOrigins, new SmallestMax<IntVar>(), new IndomainMin<IntVar>());
		System.out.println("Starting search...");
		boolean Result = label.labeling(store, select, maxEnding);

		if (Result) {
			System.out.println("Minimum time for schedule is " + maxEnding);
		} else {
			System.out.println("\n*** No");
		}

	}

}
